/*-------------------------------------------------------------------------
 - Copyright (c) 2024-2025 [XD-AMCC TEAM]
 - [XD-AMCC] is licensed under Mulan PSL v2.
 - You can use this software according to the terms and conditions of the Mulan PSL v2.
 - You may obtain a copy of Mulan PSL v2 at:
 -          http://license.coscl.org.cn/MulanPSL2
 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
 - OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 - TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 - See the Mulan PSL v2 for more details.
 ------------------------------------------------------------------------*/


/**
 * @brief API implementation for building symbol tables and symbol entries,
 *        binding all references to names,
 *        inferring all types of expressions.
 *
 * This implementation is based on class tree_listener and tree_walk().
 *
 * @author  WXQ#XDU
 * @date    2024.12
 */

#include "inside/_stb_inner.h"


// To readers:
//
// For any interesting non-terminal X, its productions,
// the semantic action may be mainly defined as two function:
//
// (1) entry_X(): pre-action, executed before visit all children of
//                a parse tree node marked by X;
// (2) exit_X():  post-action, executed after visit all children of
//                a parse tree node marked by X.
//
// All action functions for building symbol tables and entries
// are defined in inside/action*.c.
//


t_symtable_builder * symtable_builder_new (t_tree_ptr  root) {

    t_symtable_builder * pThis  = calloc(1, sizeof(*pThis));
    if (NULL == pThis) {
        ERROR_REPORT_MALLOC();
        return NULL;
    }

// Step 1:  initialize the basic object

    t_tree_listener    * listenerPtr = (t_tree_listener_ptr)pThis;
    tree_listener_init0( listenerPtr, sizeof(t_symtable_builder) );

// Step 2:  initialize my members

    pThis->treeRoot = root;
    pThis->symtableRootPtr = NULL;
    stack_init( &(pThis->scopeStack), NULL );
    pThis->nodeAttributes = treemap_new(20, stb_attrset_free);
    list_init( &(pThis->undefinedFunctions), NULL );

// Step 3:  overrides super methods (semantic action) on demand

// 3.1 methods to build symbol tables
    listenerPtr->enter_translationUnit    = stb_enter_translationUnit;
    listenerPtr->exit_translationUnit     = stb_exit_translationUnit;
    listenerPtr->enter_funcDeclaration    = stb_enter_functionDeclaration;
    listenerPtr->exit_funcDeclaration     = stb_exit_functionDeclaration;
    listenerPtr->enter_compoundStatement  = stb_enter_compoundStatement;
    listenerPtr->exit_compoundStatement   = stb_exit_compoundStatement;

// 3.2 methods to build symbol entries
    listenerPtr->exit_parameterList       = stb_exit_parameterList;
    listenerPtr->enter_parameter          = stb_enter_parameter;
    listenerPtr->exit_parameter           = stb_exit_parameter;
    listenerPtr->enter_initializer        = stb_enter_initializer;
    listenerPtr->exit_initializer         = stb_exit_initializer;
    listenerPtr->exit_directDeclarator    = stb_exit_directDeclarator;
    listenerPtr->exit_initDeclarator      = stb_exit_initDeclarator;
    listenerPtr->enter_varDeclaration     = stb_enter_variableDeclaration;
    listenerPtr->exit_varDeclaration      = stb_exit_variableDeclaration;
    listenerPtr->enter_labeledStatement   = stb_enter_labeledStatement;

// 3.3 methods to resolve name references
    listenerPtr->exit_jumpStatement       = stb_exit_jumpStatement;
    listenerPtr->exit_leftValue           = stb_exit_leftValue;
    listenerPtr->exit_functionCall        = stb_exit_functionCall;

// 3.4 inferring expression types
    listenerPtr->exit_expression            = stb_exit_expression;
    listenerPtr->exit_assignmentExpression  = stb_exit_assignmentExpr;
    listenerPtr->exit_logicalOrExpression   = stb_exit_logicalOrExpr;
    listenerPtr->exit_logicalAndExpression  = stb_exit_logicalAndExpr;
    listenerPtr->exit_equalityExpression    = stb_exit_equalityExpr;
    listenerPtr->exit_relationalExpression  = stb_exit_relationalExpr;
    listenerPtr->exit_additiveExpression    = stb_exit_additiveExpr;
    listenerPtr->exit_multiplicativeExpression  = stb_exit_multiplicativeExpr;
    listenerPtr->exit_unaryExpression       = stb_exit_unaryExpr;
    listenerPtr->exit_primaryExpression     = stb_exit_primaryExpr;

    log_debug("Symtable builder %tx has created and initialized", pThis);
    return pThis;
}


void symtable_builder_destroy (t_symtable_builder * pThis) {
    if (NULL == pThis) return;

    stack_clean ( &(pThis->scopeStack) );
    treemap_destroy ( pThis->nodeAttributes );
    list_clean( &(pThis->undefinedFunctions) );
    if (NULL != pThis->symtableRootPtr)
        symtable_destroy( pThis->symtableRootPtr );

    log_debug("Symtable builder %tx has destroyed", pThis);
    free(pThis);
}


int symtable_builder_run (t_symtable_builder * pThis) {
    if (NULL == pThis) {
        ERROR_REPORT_BUG("pass a NULL argument to symtable_builder_run()");
        return -2;
    }

    log_info( ">>> %s(): Building SYMBOL TABLES in "
              "Syntax-Directed-Translation style ...", __FUNCTION__ );
    if (NULL == pThis->treeRoot) {
        log_error( "%s: input tree is NULL", __FUNCTION__ );
        return -1;
    }

    tree_walk ( pThis->treeRoot,
                (t_tree_listener_ptr)pThis );

    // destroy symtable tree on any error
    if (pThis->nErrors > 0) {
        log_info("!!! Dump symtable tree and parse tree for study %d error%s ...",
                 pThis->nErrors,
                 (pThis->nErrors > 1 ? "s" : ""));
        symtable_dump( pThis->symtableRootPtr );
        tree_dump(pThis->treeRoot);

        symtable_destroy( pThis->symtableRootPtr );
        pThis->symtableRootPtr = NULL;
    }

    log_info( "<<< %s(): finished to build SYMBOL TABLES with %d error%s",
              __FUNCTION__ , pThis->nErrors,
              (pThis->nErrors > 1 ? "s" : "") );
    return (pThis->nErrors > 0) ? -2 : 0;
}


int symtable_builder_errors (t_symtable_builder * pThis) {
    return pThis->nErrors;
}


t_symtable_ptr symtable_builder_GRAB (t_symtable_builder * pThis) {
    t_symtable_ptr pTable = pThis->symtableRootPtr;
    pThis->symtableRootPtr = NULL;
    return pTable;
}
